package cn.lijiajia3515.cairo.auth.starter.autoconfigure;

import cn.lijiajia3515.cairo.auth.CairoAuthProperties;
import cn.lijiajia3515.cairo.security.oauth2.resource.server.web.CairoBearerTokenAuthenticationEntryPoint;
import cn.lijiajia3515.cairo.security.oauth2.resource.server.web.CairoOAuth2HttpMessageAuthenticationFailedHandler;
import cn.lijiajia3515.cairo.security.web.authentication.CairoHttpMessageAccessDeniedHandler;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.converter.Converter;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;
import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.oauth2.jwt.JwtDecoder;
import org.springframework.security.oauth2.jwt.JwtDecoders;
import org.springframework.security.oauth2.jwt.JwtValidators;
import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;
import org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationFilter;
import org.springframework.security.web.SecurityFilterChain;

/**
 * cairo auth resource server 配置
 */
@Configuration(proxyBeanMethods = false)
@AutoConfigureAfter({CairoAuthAutoConfiguration.class})
@AutoConfigureBefore(name = "org.springframework.boot.autoconfigure.security.oauth2.resource.servlet.OAuth2ResourceServerJwtConfiguration")
class CairoAuthResourceServerAutoConfiguration {

	@Configuration(proxyBeanMethods = false)
	@ConditionalOnMissingBean(JwtDecoder.class)
	static class JwtDecoderConfiguration {
		private final CairoAuthProperties properties;

		JwtDecoderConfiguration(CairoAuthProperties properties) {
			this.properties = properties;
		}

//		@Bean
//		@ConditionalOnProperty(name = "cairo.auth.base-uri")
//		JwtDecoder jwtDecoderByJwkKeySetUri() {
//			NimbusJwtDecoder nimbusJwtDecoder = NimbusJwtDecoder.withJwkSetUri(this.properties.getJwkSetUri())
//				.jwsAlgorithm(SignatureAlgorithm.from(this.properties.getJwsAlgorithm())).build();
//			String issuerUri = this.properties.getIssuerUri();
//			if (issuerUri != null) {
//				nimbusJwtDecoder.setJwtValidator(JwtValidators.createDefaultWithIssuer(issuerUri));
//			}
//			return nimbusJwtDecoder;
//		}

		@Bean
		@ConditionalOnProperty(name = "cairo.auth.base-uri")
		JwtDecoder jwtDecoderByIssuerUri() {
			return JwtDecoders.fromIssuerLocation(this.properties.getIssuerUri());
		}

	}


	/**
	 * resource server 优先
	 *
	 * @param http                        security
	 * @param authenticationEntryPoint    空认证处理
	 * @param authenticationFailedHandler 认证失败处理
	 * @param accessDeniedHandler         无权限处理
	 * @return resource 配置
	 * @throws Exception csrf异常
	 */
	@Bean
	SecurityFilterChain cairoResourceServer(HttpSecurity http,
											Converter<Jwt, AbstractAuthenticationToken> jwtAuthenticationTokenConverter,
											CairoBearerTokenAuthenticationEntryPoint authenticationEntryPoint,
											CairoOAuth2HttpMessageAuthenticationFailedHandler authenticationFailedHandler,
											CairoHttpMessageAccessDeniedHandler accessDeniedHandler) throws Exception {
		http
			.antMatcher("/api/**")
			.csrf().disable()
			.cors().and()

			.authorizeRequests(requests -> requests
				.anyRequest().authenticated()
			)
			.oauth2ResourceServer(c -> c
				.jwt()
				.jwtAuthenticationConverter(jwtAuthenticationTokenConverter)
				.and()
				.authenticationEntryPoint(authenticationEntryPoint)
				.withObjectPostProcessor(new ObjectPostProcessor<BearerTokenAuthenticationFilter>() {
					@Override
					public <O extends BearerTokenAuthenticationFilter> O postProcess(O object) {
						object.setAuthenticationFailureHandler(authenticationFailedHandler);
						return object;
					}
				}))

			.sessionManagement().disable()

			.logout().disable()

			.exceptionHandling(c -> c
				.accessDeniedHandler(accessDeniedHandler)
				.authenticationEntryPoint(authenticationEntryPoint));

		return http.build();
	}

	/**
	 * 路由白名单设置
	 *
	 * @return security config
	 */
	@Bean
	WebSecurityCustomizer cairoWebSecurityCustomizer() {
		return (web) -> web
			.ignoring()
			.antMatchers(HttpMethod.OPTIONS, "/**")
			.antMatchers("/actuator/**", "/favicon.ico", "**/favicon.ico", "/static/**", "/resources/**", "/public/**")
			;
	}
}
